package com.dly.hope.webSocket;

import com.dly.hope.constant.RedisConstant;
import com.dly.hope.modle.UserAccount;
import com.dly.hope.repository.GroupUserRepository;
import com.dly.hope.repository.WebSocketMessageRepository;
import com.dly.hope.type.MessageType;
import com.dly.hope.type.SessionType;
import com.dly.hope.utils.JsonFormatUtil;
import com.dly.hope.utils.RedisUtil;
import com.dly.hope.webSocket.dto.MessageDto;
import com.dly.hope.dto.UserSocketDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;

/**
 * @author linyu.dai
 * @project myserve
 * @description webSocket服务
 * @creat 2022/10/28 10:04:18
 */
@ServerEndpoint("/websocket/{userId}")
@Component
@Slf4j
public class WebSocketServer {

    private static final Map<String, WebSocketDto> SOCKET_MAP = new ConcurrentHashMap<>();

    //从容器中获取该对象
    private static RedisUtil redisUtil;

    private static ExecutorService executorService;

    private static WebSocketMessageRepository webSocketMessageRepository;

    private static GroupUserRepository groupUserRepository;

    @Resource
    public void setGroupUserRepository(GroupUserRepository groupUserRepositorys) {
        groupUserRepository = groupUserRepositorys;
    }

    @Resource
    public void setRedisUtil(RedisUtil redisUtils) {
        redisUtil = redisUtils;
    }

    @Resource
    public void setExecutorService(ExecutorService executorServices) {
        executorService = executorServices;
    }

    @Resource
    public void setWebSocketMessageRepository(WebSocketMessageRepository webSocketMessageRepositorys) {
        webSocketMessageRepository = webSocketMessageRepositorys;
    }


    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) throws IOException {
        log.info("用户{}, 开始链接", userId);
        //说明需要登入
        if (Boolean.FALSE.equals(redisUtil.exists(userId))) {
            //通知退出
            logOut(userId);
            session.close();
            return;
        }
        WebSocketDto webSocketDto = new WebSocketDto();
        webSocketDto.setSession(session);
        webSocketDto.setSessionId(session.getId());
        webSocketDto.setUserId(userId);
        //如果之前存在链接 关闭之前的链接
        Optional.ofNullable(SOCKET_MAP.get(userId)).ifPresent(dto -> onClose(dto.getSession(), userId));
        SOCKET_MAP.put(userId, webSocketDto);
        log.info("用户{}, 成功链接", userId);
        //添加到在线人数 队列
        redisUtil.addSet(RedisConstant.ONLINE, userId);
    }

    @OnClose
    public void onClose(Session session, @PathParam("userId") String userId) {
        log.info("用户{}, 断开链接", userId);
        //移除在线人数 队列
        redisUtil.removeSet(RedisConstant.ONLINE, userId);
        //清除socket记录
        SOCKET_MAP.remove(userId);
    }

    @OnError
    public void onError(Session session, Throwable throwable, @PathParam("userId") String userId) {
        log.info(throwable.getClass().getSimpleName(), throwable);
    }

    @OnMessage
    public void onMessage(String message, Session session, @PathParam("userId") String userId) throws IOException {
        MessageDto messageDto = JsonFormatUtil.readFromJsonString(message, MessageDto.class);
        //如果是基本消息类型
        if (messageDto.getType().equals(MessageType.HTML) || messageDto.getType().equals(MessageType.TEXT)) {
            log.info("用户{}给用户{}发送消息:  {}", userId, messageDto.getAcceptUserSocketDto().getUserId(), messageDto.getMessage());

            //如果是群消息 通知一下其他人就好了 不在线就算了
            if(messageDto.getSessionType().equals(SessionType.GROUP)){
                groupUserRepository.findAllByGroupIdAndDataStatusAndIsAgree(messageDto.getAcceptUserSocketDto().getUserId(),1,1)
                        .stream()
                        .filter(groupUser -> !Objects.equals(groupUser.getMember(), userId))
                        .forEach(groupUser -> {
                    sendMessage(message,groupUser.getMember());
                });
            }

            sendRedis(messageDto);
        }

        //如果是ping 维持用户的登入状态
        if (messageDto.getType().equals(MessageType.PING)) {
            //如果登入状态还在
            if (Boolean.FALSE.equals(redisUtil.exists(userId))) {
                //通知下线
                logOut(userId);
            }
        }

        //如果打电话
        if(messageDto.getType().equals(MessageType.TOPHONE)){
            //ring中
            if(redisUtil.getRing(RedisConstant.RING).contains(messageDto.getAcceptUserSocketDto().getUserId())){
                messageDto.setType(MessageType.RING);
                sendMessage(message, messageDto.getSendUserSocketDto().getUserId());
                return;
            }

            sendMessage(message, messageDto.getAcceptUserSocketDto().getUserId());
            //正在通话中
            redisUtil.addSet(RedisConstant.RING, messageDto.getSendUserSocketDto().getUserId());
            redisUtil.addSet(RedisConstant.RING, messageDto.getAcceptUserSocketDto().getUserId());
            log.info("用户{}打电话用户{}:  {}", userId, messageDto.getAcceptUserSocketDto().getUserId(), messageDto.getMessage());
        }

        //挂电话
        if(messageDto.getType().equals(MessageType.HANGPHONE)){
            log.info("用户{}挂电话用户{}:  {}", userId, messageDto.getAcceptUserSocketDto().getUserId(), messageDto.getMessage());
            redisUtil.removeSet(RedisConstant.RING, messageDto.getSendUserSocketDto().getUserId());
            redisUtil.removeSet(RedisConstant.RING, messageDto.getAcceptUserSocketDto().getUserId());
            sendMessage(message, messageDto.getAcceptUserSocketDto().getUserId());
        }

    }

    public static void invite(MessageDto messageDto, UserAccount send, UserAccount accept){
        //发送通知
        UserSocketDto userSocketDto1 = new UserSocketDto();
        userSocketDto1.setUserId(send.getId());
        userSocketDto1.setName(send.getAccountName());

        UserSocketDto userSocketDto2 = new UserSocketDto();
        userSocketDto2.setUserId(accept.getId());
        userSocketDto2.setName(accept.getAccountName());

        messageDto.setSendUserSocketDto(userSocketDto1);
        messageDto.setAcceptUserSocketDto(userSocketDto2);
        sendMessage(JsonFormatUtil.writeToJson(messageDto), accept.getId());
    }

    public static void sendMessage(String messageDto, String userId) {
        Optional.ofNullable(SOCKET_MAP.get(userId)).ifPresentOrElse(socketDto -> {
                    //获取到其他用户Session 发送给对应用户
                    socketDto.getSession().getAsyncRemote().sendText(messageDto);
                }, () -> {
                    log.info("用户{}不在线。。。。。", userId);
                }
        );
    }

    /**
     * @return void
     * @Author linyu.dai
     * @Description 通知下线
     * @Date 2022/11/7 17:06
     * @Param [session]
     */
    public static void logOut(String userId) {
        MessageDto messageDto = new MessageDto();
        messageDto.setType(MessageType.PING);
        //设置发送人
        UserSocketDto userSocketDto = new UserSocketDto();
        userSocketDto.setUserId(userId);
        //通知自己下线
        messageDto.setAcceptUserSocketDto(userSocketDto);
        sendMessage(JsonFormatUtil.writeToJson(messageDto), messageDto.getAcceptUserSocketDto().getUserId());
    }

    public static void sendRedis(MessageDto messageDto) {
        redisUtil.push(RedisConstant.MESSAGE_CHANNEL, JsonFormatUtil.writeToJson(messageDto));
    }
}
